Amazon Cognito
https://gyazo.com/38796bb25d8644424a1fe82b8c5104a7
概要
Cognito の日本語記事、案外少ない。簡単にいうと認証基盤を提供する AWS のサービス。
Amazon Cognito は、ウェブアプリケーションやモバイルアプリケーションの認証、許可、ユーザー管理をサポートしています。ユーザーは、ユーザー名とパスワードを使用して直接サインインするか、Facebook、Amazon、Google などのサードパーティーを通じてサインインできます。
手順
ユーザーを認証し、AWS の他のサービスに対するアクセスをユーザーに許可する手順
1. ユーザープール を介してサインインし、認証に成功するとユーザプールトークンを受け取る
2. IDプール を使用し、AWS 認証情報のユーザプールトークンを受け取る
3. AWS 認証情報を使用して AWS サービスにアクセスする
https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/images/scenario-cup-cib2.png
機能
ユーザディレクトリを作成/管理する
アプリケーションにサインアップ、アインインを追加できる
Amazon Cognito ID Pool (フェデレーティッドアイデンティティ) 外部の ID プロバイダーで認証し、AWS での一時的な ID を作成する
ここで作成した ID で他の AWS サービスにアクセスする
任意の Key-Value データを複数デバイス間で同期・編集できる (AppSync と連携)
Amazon Cognito ユーザープール
認証フロー
認証フローの種類
REFRESH_TOKEN_AUTH は、リフレッシュトークン を受け取り、ID トークン, アクセストークン を返す
USER_SRP_AUTH は、USERNAME と SRP_A を受け取り、次のチャレンジ実行のための SRP 変数を返す
USER_PASSWORD_AUTH は、USERNAME と PASSWORD を受け取り、次のチャレンジもしくはトークンを返す
USERNAME, PASSWORD をダイレクトにやり取りする
チャレンジの種類
SMS_MFA
PASSWORD_VERIFIER
CUSTOM_CHALLENGE
DEVICE_SRP_AUTH
DEVICE_PASSWORD_VERIFIER
NEW_PASSWORD_REQUIRED
クライアント側のフローは以下のようになっているようだ。
1. ユーザは、アプリにユーザ名, パスワードを入力する
3. SDK は、レスポンスとして、取り組むべきチャレンジの情報を得る
5-a. それ以上チャレンジがない場合、SDK は、レスポンスとして、トークンを取得する
5-b. それ以上チャレンジが続く場合、 4 に戻る
一方、安全なサーバーサイドアプリが認証を実施する場合は、別のフローを辿れるという。
2. アプリは、レスポンスとして、認証パラメータを取得する
これを有効にするためには、以下のいずれか1つを満たしている必要がある。
CreateUserPoolClient, UpdateUserPoolClient の呼び出し時に、ExplicitAuthFlow パラメータに ADMIN_NO_SRP_AUTHENTICATION を渡す
ユーザプールの作成時に、サーバーベースの認証でサインイン API を有効にする (ADMIN_NO_SRP_AUTH) をチェックしておく
セッションの永続化を行いたい場合
refresh token を保存しておけば良い。
amazon-cognito-identity-js を利用する場合は、case 32 が該当する。使用する API は initiateAuth ではなく refreshSession になるようだ。
Attribute
Cognito User Pool では、登録したユーザにひもづく 属性 を設定できる。OpenID Connect 仕様に従って実装されている、とのことで、既存の標準属性は以下に列挙されている。
特に、電話番号と Email については、その値を検証するというステップが必要になる。属性でいうと、Email であればそれ自体が格納される属性である email に対し、その値が検証済みか?を示す属性である email_verified が存在する。
Amazon Cognito ID プール
Amazon Cognito ID
認証情報 ではない
AWS STS でサポートされている Web ID Federation を通して、AWS 認証情報と交換するための ID
AWS.CognitoIdentityCredentials を使用して取得するのが推奨されている
AWS.CognitoIdentityCredentials は、上記の「STS の Web ID Federation を利用した AWS 認証情報との交換」を行なってくれるクラス。正確には、その交換した AWS 認証情報を表現しているオブジェクト。
Represents credentials retrieved from STS Web Identity Federation using the Amazon Cognito Identity service.
IdentityId と IdentityPoolId という二種類の用語が登場しているが、前者が Amazon Cognito ID、後者が ID プールの ID だと思われる。IdentityId については以下のように記述がある。
The Cognito ID returned by the last call to AWS.CognitoIdentity.getOpenIdToken().
実際にこれを利用して AWS 認証情報を取得する流れは以下のようになる (公式からの引用)。
code:javascript
AWS.config.region = 'us-east-1';
// ID プールを利用するために、Credentials Provider を設定する
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'IDENTITY_POOL_ID',
Logins: {
'www.amazon.com': 'AMAZONTOKEN',
'accounts.google.com': 'GOOGLETOKEN'
}
});
// AWS 認証情報を取得する
AWS.config.credentials.get(function(){
var accessKeyId = AWS.config.credentials.accessKeyId;
var secretAccessKey = AWS.config.credentials.secretAccessKey;
var sessionToken = AWS.config.credentials.sessionToken;
});
特に、UserPool と組み合わせる場合は以下のようになる。Logins オプションに UserPool の認証情報を追加すれば良い。
code:javascript
var cognitoUser = userPool.getCurrentUser();
if (cognitoUser != null) {
cognitoUser.getSession(function(err, result) {
if (result) {
console.log('You are now logged in.');
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'YOUR_IDENTITY_POOL_ID',
Logins: {
'cognito-idp.<region>.amazonaws.com/<YOUR_USER_POOL_ID>': result.getIdToken().getJwtToken()
}
});
}
});
}
UserPool を使った認証の流れ。
code:typescript
// あるユーザプール内の特定ユーザを表す CognitoUser の用意
const userPool = new CognitoUserPool({
UserPoolId: <ユーザプールID>,
ClientId: <クライアントID>
});
const userData = {
Username: userName,
Pool: userPool
};
const cognitoUser = new CognitoUser(userData);
// CognitoUser の認証情報を用意
const authenticationData = {
Username: userName,
Password: passWord
};
const authenticationDetails = new AuthenticationDetails(authenticationData);
// 最終的には、CognitoUserSession を返す
return new Promise(
(
resolve: (value?: session) => void,
reject: (reason?: string) => void
) => {
const callback: IAuthenticationCallback = {
onSuccess(result) {
resolve(result);
},
onFailure(err) {
reject(err);
},
// 初回はパスワードの確認を求められる
newPasswordRequired: (userAttributes, requiredAttributes) => {
delete userAttributes.email_verified;
cognitoUser.completeNewPasswordChallenge(
// パスワードの変更は行わない (仮パスワードと同じパスワードを再設定する)
passWord,
userAttributes,
callback
);
}
};
// CognitoUser に対して認証を行い、結果を callback がハンドリングする
cognitoUser.authenticateUser(authenticationDetails, callback);
}
).then(data => {
this.storeRefreshToken({
refreshToken: data.getRefreshToken().getToken()
});
return new Promise((resolve: (value: session) => void) =>
resolve(data as session)
);
});
code:typescript
export class CognitoUserSession {
constructor(data: ICognitoUserSessionData);
public getIdToken(): CognitoIdToken;
public getRefreshToken(): CognitoRefreshToken;
public getAccessToken(): CognitoAccessToken;
public isValid(): boolean;
}
/*
export class CognitoIdentityServiceProvider {
public config: AWS.CognitoIdentityServiceProvider.Types.ClientConfiguration;
}
*/
export class CognitoAccessToken {
constructor({ AccessToken }: { AccessToken: string });
public getJwtToken(): string;
public getExpiration(): number;
public getIssuedAt(): number;
}
export class CognitoIdToken {
constructor({ IdToken }: { IdToken: string });
public getJwtToken(): string;
public getExpiration(): number;
public getIssuedAt(): number;
}
export class CognitoRefreshToken {
constructor({ RefreshToken }: { RefreshToken: string });
public getToken(): string;
}
各トークンについて。
ID トークン には、name、email、phone_number といった、認証されたユーザーの ID に関するクレームが含まれます。
アクセストークン は、認証されたリソースへのアクセスを付与します。
更新トークン には、新しい ID またはアクセストークンの取得に必要な情報が含まれます。
Amazon Cognito ユーザープールには、OpenID Connect (OIDC) オープン標準で定義されている ID トークン、アクセストークン、および更新トークンが実装されています。